//@author A0101119Y
#include "DateTime.h"
using namespace Twentyfifth;
using namespace std;

DateTime::DateTime() {
	year = BLANK_DATE;
	month = BLANK_DATE;
	day = BLANK_DATE;
	hour = BLANK_TIME;
	minute = BLANK_TIME;
	second = 0;
}

DateTime::DateTime(int newHour, int newMinute) {
	year = BLANK_DATE;
	month = BLANK_DATE;
	day = BLANK_DATE;
	hour = newHour;
	minute = newMinute;
	second = 0;
}

DateTime::DateTime(int newDay, int newMonth, int newYear) {
	year = newYear;
	month = newMonth;
	day = newDay;
	hour = BLANK_TIME;
	minute = BLANK_TIME;
	second = 0;
}


DateTime::DateTime(int newHour, int newMinute, int newDay, int newMonth, int newYear) {
	year = newYear;
	month = newMonth;
	day = newDay;
	hour = newHour;
	minute = newMinute;
	second = 0;
}

bool DateTime::operator==(const DateTime& rhs) const {
	int thisYear = this->year;
	int thisMonth = this->month;
	int thisDay = this->day;
	int thisHour = this->hour;
	int thisMinute = this->minute;
	int thisSecond = this->second;
	int thatYear = rhs.getYear();
	int thatMonth = rhs.getMonth();
	int thatDay = rhs.getDay();
	int thatHour = rhs.getHour();
	int thatMinute = rhs.getMinute();
	int thatSecond = rhs.getSecond();

	if (thisYear == thatYear) {
		if (thisMonth == thatMonth) {
			if (thisDay == thatDay) {
				if (thisHour == thatHour) {
					if (thisMinute == thatMinute) {
						if (thisSecond == thatSecond) {
							return true;
						}
					}
				}
			}
		}
	}

	return false;
}

bool DateTime::operator!=(const DateTime& rhs) const {
	return !operator==(rhs);
}

bool DateTime::operator< (const DateTime& rhs) const {
	int thisYear = this->year;
	int thisMonth = this->month;
	int thisDay = this->day;
	int thisHour = this->hour;
	int thisMinute = this->minute;
	int thisSecond = this->second;
	int thatYear = rhs.getYear();
	int thatMonth = rhs.getMonth();
	int thatDay = rhs.getDay();
	int thatHour = rhs.getHour();
	int thatMinute = rhs.getMinute();
	int thatSecond = rhs.getSecond();
	//this < that
	if (thisYear < thatYear) {
		return true;
	} else if (thisYear == thatYear) {
		if (thisMonth < thatMonth) {
			return true;
		} else if (thisMonth == thatMonth) {
			if (thisDay < thatDay) {
				return true;
			} else if (thisDay == thatDay) {
				if (thisHour < thatHour) {
					return true;
				} else if (thisHour == thatHour) {
					if (thisMinute < thatMinute) {
						return true;
					} else if (thisMinute == thatMinute) {
						if (thisSecond < thatSecond) {
							return true;
						} // if second smaller
					} // if minute same
				} // if hour same
			} // if day same
		} // if month same
	} // if year same

	return false;

}

bool DateTime::operator>(const DateTime& rhs) const {
	DateTime thisDate(hour, minute, day, month, year);
	thisDate.setSecond(second);
	DateTime thatDate(rhs.getHour(), rhs.getMinute(), rhs.getDay(), rhs.getMonth(), rhs.getYear());
	thatDate.setSecond(rhs.getSecond());
	return thatDate.operator<(thisDate);
}

bool DateTime::operator<=(const DateTime& rhs) const {
	return !operator>(rhs);
}

bool DateTime::operator>=(const DateTime& rhs) const {
	return !operator<(rhs);
}

//@author A0102127Y
string DateTime::toString() {
	char buffer[500];
	sprintf_s(buffer, "%02i:%02i %02i/%02i/%04i", hour, minute, day, month, year);
	string feedback = buffer;
	return feedback;
}

void DateTime::print() {
	printf("%02i:%02i %02i/%02i/%04i", hour, minute, day, month, year);
}

//@author A0101119Y
const int DateTime::getYear() const {
	return year;
}

const int DateTime::getMonth() const {
	return month;
}

const int DateTime::getDay() const {
	return day;
}

const int DateTime::getHour() const {
	return hour;
}

const int DateTime::getMinute() const {
	return minute;
}

const int DateTime::getSecond() const {
	return second;
}

void DateTime::setYear(int newYear) {
	year = newYear;
}

void DateTime::setMonth(int newMonth) {
	month = newMonth;
}

void DateTime::setDay(int newDay) {
	day = newDay;
}

void DateTime::setHour(int newHour) {
	hour = newHour;
}

void DateTime::setMinute(int newMinute) {
	minute = newMinute;
}

void DateTime::setSecond(int newSecond) {
	second = newSecond;
}

bool DateTime::isBlankTime() {
	if ((hour == BLANK_TIME) ||
		(minute == BLANK_TIME)) {
		return true;
	}
	return false;
}

bool DateTime::isBlankDate() {
	if ((year == BLANK_DATE) ||
		(month == BLANK_DATE) ||
		(day == BLANK_DATE)) {
		return true;
	}
	return false;
}

//@author A0101906R
bool DateTime::isValidDate(int day, int month, int year) {
	int daysForLeapYear[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	int daysForNormalYear[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
	bool leapYear;

	leapYear = isLeapYear(year);

	if (month > 12 || month < 0 || day < 0 || year < 0)
		return false;
	else if (leapYear &&  day > daysForLeapYear[month])
		return false;
	else if (!leapYear && day > daysForLeapYear[month])
		return false;
	else
		return true;
}

bool DateTime::isValidDate() {
	return isValidDate(day, month, year);
}

bool DateTime::isLeapYear(int year) {
	if (year % 4 == 0) {
		if ((year % 100 != 0) || (year % 400 == 0)) {
			return true;
		}
	}
	return false;
}
bool DateTime::isValidTime(int hr, int min, int sec) {
	if (hr > 24 || hr < 0) {
		return false;
	}
	if (min > 60 || min < 0) {
		return false;
	}
	if (sec > 60 || sec < 0) {
		return false;
	}
	return true;
}

bool DateTime::isValidTime() {
	return isValidTime(hour, minute, second);
}

//@author A0101119Y
void DateTime::setToNow() {
	struct tm newtime;
	__time64_t long_time;
	errno_t err;
	_time64(&long_time);
	err = _localtime64_s(&newtime, &long_time);
	if (err) {
		cerr << "ERROR: cannot get current date/time." << endl;
	} else {
		setDay(newtime.tm_mday);
		setMonth(newtime.tm_mon + 1);
		setYear(newtime.tm_year + 1900);
		setHour(newtime.tm_hour);
		setMinute(newtime.tm_min);
		setSecond(newtime.tm_sec);
	}
}

void DateTime::offset(int secondsOffset) {
	struct tm newtime = { 0 };
	newtime.tm_year = year - 1900;
	newtime.tm_mon = month - 1;
	newtime.tm_mday = day;
	newtime.tm_hour = hour;
	newtime.tm_min = minute;
	newtime.tm_sec = second;
	__time64_t epochTime = _mktime64(&newtime);
	epochTime += secondsOffset;
	errno_t err = _localtime64_s(&newtime, &epochTime);
	if (err) {
		cerr << "ERROR: cannot offset date/time." << endl;
	} else {
		setDay(newtime.tm_mday);
		setMonth(newtime.tm_mon + 1);
		setYear(newtime.tm_year + 1900);
		setHour(newtime.tm_hour);
		setMinute(newtime.tm_min);
		setSecond(newtime.tm_sec);
	}
}

__time64_t DateTime::getUnixEpochTime() {
	if (!isValidDate() || !isValidTime()) {
		return 0;
	}
	struct tm newtime = { 0 };
	newtime.tm_year = year - 1900;
	newtime.tm_mon = month - 1;
	newtime.tm_mday = day;
	newtime.tm_hour = hour;
	newtime.tm_min = minute;
	newtime.tm_sec = second;
	__time64_t epochTime = _mktime64(&newtime);
	return epochTime;
}